<?php
/**
 * urls data structure.
 * parses url string, allows to get/set any part :
 * <ul>
 * <li>scheme</li>
 * <li>host</li>
 * <li>base path</li>
 * <li>relative path</li>
 * <li>query vars</li>
 * <li>fragment</li>
 * </ul>
 * This file belongs to a webappkit package (kit)
 * @link http://webappkit.net
 * @package databeans
 * @subpackage http
 * @author J.Ducastel <nospam0@ducastel.name>
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 */
class UrlBean extends DataBean {
/*------------------------------------------------------------------------------
                                                                     constructor
------------------------------------------------------------------------------*/
    /**
    * constructor alias
    * @see urlbean::__construct()
    * @access public
    */
    function UrlBean($url=NULL,$base=NULL) {
        $this->__construct($url,$base);
    }/**/
    /**
    * "real" constructor.
    * @access public
    * @param string $url
    * @param string $base base url for relative urls, as in <base href>
    */
    function __construct($url=NULL,$base=NULL) {
        $this->membersLock();
        $parts=$this->parseString($url); //print_r($parts);
        if ($parts) {
            $this->loadData($parts);
        }
        if ($base)
            $this->setbasepath($base,true);
        // echo '<pre>'; print_r($this);
    }

    function __toString() {
        return $this->getAbsolute();
    }
/*------------------------------------------------------------------------------
                                                                  public methods
------------------------------------------------------------------------------*/
    /**
    * parse an url string.
    * @access public
    * @param string $string url as a string
    * @static
    * @return array parsed string
    */
    function parseString($string=NULL) {

        $data=parse_url($string); //print_r($data);
        $data['scheme']=isset($data['scheme'])?$data['scheme']:null;
        //$data['host']=isset($data['host'])?$data['host']:null;
        // parsing query
        $getVars=isset($data['query'])?explode('&',$data['query']):array();
        $data['vars']=array();
        foreach ($getVars as $var) {
            if (!$var) continue;
            $var=explode('=',$var);
            $data['vars'][$var[0]]=$var[1];
        }
        // parsing fragment
        if (ereg('#(.+)$',$string,$search))
            $data['fragment']=$search[1];
        else $data['fragment']=null;
        // echo wakaffvar($data);
        return $data;
    }/**/

    /**
    * returns the relative url (without base).
    * @access public
    * @return string
    */
    function getRelative() {
        $path=$this->getPath();
        $query=$this->getquery();
        $fragment=$this->getfragment();
        return $path
        .($query?'?'.$query:'')
        .($fragment?'#'.$fragment:'');
    }/**/

    /**
    * returns the full absolute url
    * @access public
    * @return string
    */
    function getAbsolute() {
        $scheme=$this->getscheme();
        $host=$this->getHost();
        $basepath=$this->getBasePath();
        $path=$this->getPath();
        $query=$this->getquery();
        $fragment=$this->getfragment();
        return $scheme.'://'
        .$host.'/'
        .($basepath?$basepath.'/':'')
        .$path
        .($query?'?'.$query:'')
        .($fragment?'#'.$fragment:'');
    }/**/

    /**
    * returns the query part (without ?)
    * do not converts & to &amp;
    * @access public
    * @return string
    */
    function getQuery() {
        $query='';
        foreach($this->vars as $key => $value) {
            $query.="$key=$value&";
        }
        // wiping last unnecessary &amp;
        $query=substr($query,0,-1);
        return $query;
    }/**/

    /**
    * return current $scheme value
    * @access public
    * @return string current $scheme
    */
    function getScheme() {
        return $this->getMember('scheme');
    }/**/

    /**
    * set $scheme value
    * @access public
    * @return string current $scheme
    */
    function setScheme($scheme=null) {
        return $this->setMember('scheme',$scheme);
    }/**/

    /**
    * return current $host value
    * @access public
    * @return string current $host
    */
    function gethost() {
        return $this->getMember('host');
    }/**/

    /**
    * set $host value
    * @access public
    * @return string current $host
    */
    function sethost($host=null) {
        return $this->setMember('host',$host);
    }/**/

    /**
    * return current $path value
    * @access public
    * @return string current $path
    */
    function getpath() {
        return $this->getMember('path');
    }/**/

    /**
    * sets $path value
    * @access public
    * @return string current $path
    */
    function setpath($path) {
        return $this->setMember('path',$path);
    }

    /**
     * splits path part into segments separated by /
     * @access public
     * @return array
     */
    function splitPath() {
        return explode('/',$this->get('path'));
    }

    /**
    * return $index segment of uri.
    * i.e. part after $index number of slashes and before next slash
    * @access public
    * @param int $index
    * @return mixed string|null
    */
    function getUriSegment($index) {
        if (!$path=$this->getMember('path'))
            return null;
        $segments=explode('/',$path);
        return isset($segments[$index])?$segments[$index]:null;
    }

    /**
    * return current $basepath value
    * @access public
    * @return bool
    */
    function getBasePath() {
        return $this->getMember('basepath');
    }/**/

    /**
    * sets $basepath value
    * @param string $base base url
    * @access public
    * @return bool
    */
    function setbasePath($basepath=null) {
        return $this->set('basepath',$basepath);
    }/**/

    /**
    * return $vars[$var] value
    * @access public
    * @param string $key var's key
    * @return mixed usually a string
    */
    function getvar($key) {
        if (!isset($this->vars[$keys])) return null;
        return $this->vars[$key];
    }/**/

    /**
    * sets $vars[$var] value
    * @access public
    * @param $key string var's key
    * @param mixed $value var's new value
    * @return string current $scheme
    */
    function setvar($key, $value) {
        $this->vars[$key]=$value;
        return true;
    }/**/

    /**
    * return current $vars as an associative array
    * @access public
    * @return array
    */
    function getvars() {
        return $this->get('vars');
    }/**/

    /**
    * sets all vars form a n associative array
    * @access public
    * @param array $vars
    * @return bool
    */
    function setvars($vars) {
        return $this->setMember('vars',$vars);
    }/**/

    /**
    * return current $fragment value
    * @access public
    * @return string current $scheme
    */
    function getfragment() {
        return $this->get('fragment');
    }/**/
    /**
    * sets $fragment value
    * @access public
    * @return string current $scheme
    */
    function setfragment($fragment) {
        return $this->setMember('fragment',$fragment);
    }/**/

/*------------------------------------------------------------------------------
                                                                      properties
------------------------------------------------------------------------------*/
    //var $_databean_members_lock=true;

    /**
    * @var type desc
    */
    var $scheme='http';
    function _scheme_check($x) {
        return in_array($x,array('http','https','ftp'));
    }
    /**
    * @var type desc
    */
    var $host;
    function _host_check($x) {
        return ereg('^[a-zA-Z0-9\.-]+$',$x);
    }
    /**
    * @var string base path, part ignored for relative path
    * (= physical path to index)
    */
    var $basepath;
    function _basepath_check($x) {
        return ereg('^[a-zA-Z0-9_/\.-]+$',$x);
    }
    function _basepath_set($base) {
        $base=trim($base,'/');
        // checking
        if (!$this->_basepath_check($base))
            return false;
        // adjusting relative path
        // what is the full path ? base + relative
        $full=($this->basepath?$this->basepath.'/':'')
        .$this->path;
        //echo "<p>full = $full</p>";
        // checking that the new relative path is included in current path
        if (substr($full,0,strlen($base))!=$base) {
            return false;
        }
        $newpath=substr($full,strlen($base)+1);
        $this->path=$newpath;
        // echo "<p>newpath=$newpath</p>";
        $this->basepath=$base;
        return true;
    }
    /**
    * @var string relative path (not starting by /)
    */
    var $path;
    function _path_check($x) {
        return ereg('^[a-zA-Z0-9_/\.-]*$',$x);
    }
    function _path_inputfilter($path) {
        $newpath=trim($path,'/'); //echo "$path >> $newpath";
        return $newpath;
    }/**/

    /**
    * @var array GET vars
    */
    var $vars;
    function _vars_check($x) {
        return is_array($x);
    }
    //function _vars
    /**
    * @var type desc
    */
    var $fragment;
    function _fragment_check($x) {
        return is_string($x) and ereg('^[a-zA-Z][a-zA-Z0-9_-]*$',$x);
    }
    function _fragment_inputfilter($fragment) {
        if (is_string($fragment))
            $fragment=str_replace('#','',$fragment);
        return $fragment;
    }

    /**
    * @var type desc
    */
    //var //
}